/**
 * @file	
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */


#include "bcomdef.h"
#include "linkdb.h"
#include "types.h"
#include "gatt.h"
#include "gapgattserver.h"
#include "gattservapp.h"
#include "gatt_uuid.h"
#include "peripheral.h"
#include "gapbondmgr.h"
#include "gatt_profile_uuid.h"
#include "ble_ancs.h"
#include "ancs_attr.h"
#include "log.h"


static uint8_t Ancs_CCCDConfig(uint16_t attrHdl, uint8_t isEnable);

static ancs_ctx_t s_ancs_ctx;

bStatus_t ble_ancs_attr_add(const ancs_notif_attr_id id, uint8_t * p_data, const uint16_t len)
{
  ancs_attr_list_t* p_ancs_notif_attr_list = s_ancs_ctx.notif_attr_list;
  
  if(!p_data)
    return INVALIDPARAMETER;

  if((len == 0) || (len > ANCS_ATTR_DATA_MAX))
  {
        return INVALID_MEM_SIZE;
  }

  p_ancs_notif_attr_list[id].en         = true;
  p_ancs_notif_attr_list[id].attr_len    = len;
  p_ancs_notif_attr_list[id].p_attr_data = p_data;

  return SUCCESS;
}

static uint8_t figure_out_chars_end_hdl(void)
{
  uint8_t errorcode = 0;
  ancs_ctx_t* pctx = &s_ancs_ctx;
  ancs_service_t* pservie = &(s_ancs_ctx.ancs_service);
  uint16_t* p_chars_hdl = pservie->chars_hdl;
  uint16_t ntf, cp, dt;
  ntf = p_chars_hdl[ANCS_NOTIF_SCR_HDL_START];
  cp = p_chars_hdl[ANCS_CTRL_POINT_HDL_START];
  dt = p_chars_hdl[ANCS_DATA_SRC_HDL_START];
  
  if(ntf < ((cp < dt) ? cp :dt))
    p_chars_hdl[ANCS_NOTIF_SCR_HDL_END] = cp > dt ? dt : cp;
  else if(ntf < ((cp < dt) ? dt :cp))
    p_chars_hdl[ANCS_NOTIF_SCR_HDL_END] = cp > dt ? cp : dt;
  else
    p_chars_hdl[ANCS_NOTIF_SCR_HDL_END] = pservie->service_hdl[1];
  
  if(cp < ((ntf < dt) ? ntf:dt))
    p_chars_hdl[ANCS_CTRL_POINT_HDL_END] = ntf > dt ? dt : ntf;
  else if(cp < ((ntf < dt) ? dt :ntf))
    p_chars_hdl[ANCS_CTRL_POINT_HDL_END] = ntf > dt ? ntf : dt;
  else
    p_chars_hdl[ANCS_CTRL_POINT_HDL_END] = pservie->service_hdl[1];
  
  if(dt < ((ntf < cp) ? ntf :cp))
    p_chars_hdl[ANCS_DATA_SRC_HDL_END] = ntf > cp ? cp : ntf;
  else if(dt < ((ntf < cp) ? cp :ntf))
    p_chars_hdl[ANCS_DATA_SRC_HDL_END] = ntf > cp ? ntf : cp;
  else
    p_chars_hdl[ANCS_DATA_SRC_HDL_END] = pservie->service_hdl[1];

  // Sanity check to ensure that each start handle is valid and
  // less than each respective end handle.
  if(p_chars_hdl[ANCS_NOTIF_SCR_HDL_START]  != 0 &&
     p_chars_hdl[ANCS_CTRL_POINT_HDL_START] != 0 &&
     p_chars_hdl[ANCS_DATA_SRC_HDL_START]   != 0)
  {
    if(p_chars_hdl[ANCS_NOTIF_SCR_HDL_START]  < p_chars_hdl[ANCS_NOTIF_SCR_HDL_END]  &&
       p_chars_hdl[ANCS_CTRL_POINT_HDL_START] < p_chars_hdl[ANCS_CTRL_POINT_HDL_END] &&
       p_chars_hdl[ANCS_DATA_SRC_HDL_START]   < p_chars_hdl[ANCS_DATA_SRC_HDL_END])
    {
      LOG("All chars discoveried\n");
    }
    else
    {
      LOG("ANCS_STORE_CHARS_HANDLES FAILURE\n");
      pctx->disc_state = ANCS_DISC_FAILED;
      errorcode = 4;
    }
  }
  // Throw an error if the handles are invalid.
  else
  {
    LOG("ANCS_STORE_CHARS_HANDLES FAILURE\n");
    pctx->disc_state = ANCS_DISC_FAILED;
    errorcode = 5;
  }

  return errorcode;
}


static bStatus_t ble_disc_service(gattMsgEvent_t *pMsg)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  ancs_service_t* pservie = &(pctx->ancs_service);
  
  // Stores the error code, should the discovery process fail at any state.
  uint8_t errorcode = 0;
  //if(pMsg){
  //  LOG("ble_disc_service->pMsg->method: 0x%x\n",pMsg->method);
  //}
  // Enter the state machine.
  switch (pctx->disc_state)
  {
    case ANCS_UNINIT:
      LOG("Discovery Progress:\t1\n");
      return FAILURE;
    
    // Perform a GATT Discover Primary Service By Service UUID to located the ANCS
    // handles.
    case ANCS_DISC_SERVICE:
    {
      LOG("Discovery Progress:\t2\n");
      LOG("Discovery State:\tDiscover the ANCS\n");
      // Initialize the ANCS handles to zero.
      pservie->service_hdl[0] = 0;
      pservie->service_hdl[1] = 0;

      // Store the ANCS UUID for GATT request.
      uint8_t uuid[ATT_UUID_SIZE] = {ANCSAPP_ANCS_SVC_UUID};

      // Discover the ANCS by UUID.
      bStatus_t ret = GATT_DiscPrimaryServiceByUUID(pservie->conn_hdl , uuid, ATT_UUID_SIZE, pctx->app_task_ID);

      // If successfully discovered proceed, throw error if not.
      if(ret == SUCCESS){
        pctx->disc_state = ANCS_STORE_SERVICE_HANDLES;
        pservie->expect_type_value_num = 0;
        //ancsAppState = ANCS_STATE_READY;
      }
      else
      {
        LOG("ANCS_DISC_SERVICE FAILURE, Error code:\t%d\n", ret);
        pctx->disc_state = ANCS_DISC_FAILED;
        errorcode = 1;
      }
    }
    break;

    // Store the ANCS handles requested in the previous state.
    case ANCS_STORE_SERVICE_HANDLES:
    {
      LOG("Discovery Progress:\t3\n");
      LOG("Discovery State:\tStore the ANCS handles %d\n",pMsg->method);
      // Did the application receive a response from the GATT Disc Primary Service?
      if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP )
      {
        LOG("found :%d\n",pMsg->msg.findByTypeValueRsp.numInfo);
        
        // Check if the ANCS was found.
        if (pMsg->msg.findByTypeValueRsp.numInfo > 0)
        {
          // Found the ANCS, so store the handles and proceed.
          pservie->service_hdl[0]  = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;
          pservie->service_hdl[1]  = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle;
          pctx->disc_state = ANCS_DISC_CHARS;
          //for(i = 0; i< pMsg->msg.findByTypeValueRsp.numInfo; i++){
          //  pservie->service_hdl[pservie->expect_type_value_num]  = pMsg->msg.findByTypeValueRsp.handlesInfo[i].handle;
          //  pservie->expect_type_value_num ++;
          //  if(pservie->expect_type_value_num == 2){
          //    pctx->disc_state = ANCS_DISC_CHARS;
          //    break;
          //  }
          //}
        }
        else
        {
          // The ANCS was not found.
          LOG("ANCS_STORE_SERVICE_HANDLES FAILURE\n");
          pctx->disc_state = ANCS_DISC_FAILED;
          errorcode = 2;
        }

      }
      else
      {
        LOG("ble_disc_service->pMsg->method: 0x%x\n",pMsg->method);
        LOG("ANCS_STORE_SERVICE_HANDLES FAILURE\n");
        pctx->disc_state = ANCS_DISC_FAILED;
        errorcode = 2;

      }
    }
    break;

    // Use the ANCS handles to discovery the ANCS's characteristics' handles.
    case ANCS_DISC_CHARS:
    {
      LOG("Discovery Progress:\t4\n");
      LOG("Discovery State:\tDiscover the ANCS characteristics\n");
      // Check if service handle discovery event has completed.
      if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP )
      {
        LOG("ble_disc_service->pMsg->method: 0x%x\n",pMsg->method);
        if(pMsg->hdr.status == bleProcedureComplete)
        {
          // Sanity check to make sure the handle is valid before proceeding.
          if (pservie->service_hdl[0] != 0 && pservie->service_hdl[1] != 0 )
          {
            // Discover all characteristics of the ANCS.
            bStatus_t ret = GATT_DiscAllChars(
                                  pservie->conn_hdl,
                                  pservie->service_hdl[0],
                                  pservie->service_hdl[1],
                                  pctx->app_task_ID);
            pservie->chars_disc_num = 0;

            // If the request was successfully sent, proceed with the discovery process.
            if (ret == SUCCESS)
            {
              pctx->disc_state = ANCS_STORE_CHARS_HANDLES;
            }
            // If not, throw an error.
            else
            {
              LOG("ANCS_DISC_CHARS FAILURE, Error code:\t%d\n",ret);
              pctx->disc_state = ANCS_DISC_FAILED;
              errorcode = 3;
            }
          }
        }
      }
      else
      {
        LOG("ble_disc_service->pMsg->method: 0x%x\n",pMsg->method);
      }
    }
    break;

    // Store the retrieved ANCS characteristic handles.
    case ANCS_STORE_CHARS_HANDLES:
    {
      LOG("Discovery Progress:\t5\n");
      LOG("Discovery State:\tStore the ANCS characteristics' handles\n");
      // Wait until GATT "Read by type response" is received, then confirm that the correct number of
      // pairs are present, and that their length is correct
      if (pMsg->method == ATT_READ_BY_TYPE_RSP )
      {
        //if ( (pMsg->msg.readByTypeRsp.numPairs == NUMBER_OF_ANCS_CHARS) && (pMsg->msg.readByTypeRsp.len == CHAR_DESC_HDL_UUID128_LEN) )
        if ((pMsg->msg.readByTypeRsp.len == CHAR_DESC_HDL_UUID128_LEN) )
        {
          // Pointer to the pair list data in the GATT response.
          uint8_t   *pCharPairList;
          // Will store the start and end handles of the current pair.
          uint16_t  charStartHandle;
          // Stores to the UUID of the current pair.
          uint16_t  charUuid;
          // Stores what pair the loop is currently processing.
          uint8_t   currentCharIndex;

          // Set the pair pointer to the first pair.
          pCharPairList = pMsg->msg.readByTypeRsp.dataList;

          // Iterate through all three pairs found.
          for(currentCharIndex = 0; currentCharIndex < pMsg->msg.readByTypeRsp.numPairs ; currentCharIndex++)
          {
            uint16_t* p_chars_hdl = pservie->chars_hdl;
            // Extract the starting handle, ending handle, and UUID of the current characteristic.
            charStartHandle = BUILD_UINT16(pCharPairList[3], pCharPairList[4]);
            charUuid        = BUILD_UINT16(pCharPairList[5], pCharPairList[6]);
            LOG("Chars found handle is is %d, uuid is %x\n", charStartHandle, charUuid);
            // Store the start and end handles in the handle cache corresponding to
            // their UUID.
            switch (charUuid)
            {
              // If it's the Notification Source.
              case ANCSAPP_NOTIF_SRC_CHAR_UUID:
                p_chars_hdl[ANCS_NOTIF_SCR_HDL_START] = charStartHandle;
                p_chars_hdl[ANCS_NOTIF_SCR_HDL_END]   = 0;
                break;
              // If it's the Control Point.
              case ANCSAPP_CTRL_PT_CHAR_UUID:
                p_chars_hdl[ANCS_CTRL_POINT_HDL_START] = charStartHandle;
                p_chars_hdl[ANCS_CTRL_POINT_HDL_END]   = 0;
                break;
              // If it's the Data Source.
              case ANCSAPP_DATA_SRC_CHAR_UUID:
                p_chars_hdl[ANCS_DATA_SRC_HDL_START] = charStartHandle;
                p_chars_hdl[ANCS_DATA_SRC_HDL_END]   = 0;
                break;

              default:
                break;
            }
            pservie->chars_disc_num ++;

            // If this is the final characteristic found in the response,
            // reset its end handle to the ANCS's end handle. This is because
            // there is no next staring handle to use as a reference and subtract one
            // from, so instead the ending handle of the ANCS must be used.
            if(pservie->chars_disc_num == NUMBER_OF_ANCS_CHARS){

              errorcode = figure_out_chars_end_hdl();
              pctx->disc_state = (errorcode == 0) ? ANCS_DISC_NS_DESCS : ANCS_DISC_FAILED; 
            }

            // Increment the pair pointer to the next pair.
            pCharPairList += CHAR_DESC_HDL_UUID128_LEN;
          }
        }
        // Throw an error if the length or number of pairs is incorrect.
        else
        {
          LOG("ANCS_STORE_CHARS_HANDLES FAILURE\n");
          pctx->disc_state = ANCS_DISC_FAILED;
          errorcode = 6;
        }
      }
      else
      {
        LOG("ble_disc_service->pMsg->method: 0x%x\n",pMsg->method);
        if(pMsg->method == ATT_ERROR_RSP){
          attErrorRsp_t* perr = &(pMsg->msg.errorRsp);
          LOG("ATT_ERROR_RSP 0x%x, 0x%x, 0x%x\n", perr->errCode, perr->handle, perr->reqOpcode);
        }
      }
    }
    break;

    // Discover the Notification Source's descriptors (namely, the CCCD) using the start
    // and end handle stored in the handle cache.
    case ANCS_DISC_NS_DESCS:
    {
      LOG("Discovery Progress:\t6\n");
      LOG("Discovery State:\tDiscover the Notification Source's CCCD\n");
      
      // Wait until the characteristic handle discovery has finished.
      if ( (pMsg->method == ATT_READ_BY_TYPE_RSP) && (pMsg->hdr.status == bleProcedureComplete) )
      {
        // Discover the ANCS Notification Source descriptors.
        bStatus_t ret = GATT_DiscAllCharDescs(pservie->conn_hdl,
                              pservie->chars_hdl[ANCS_NOTIF_SCR_HDL_START],
                              pservie->chars_hdl[ANCS_NOTIF_SCR_HDL_END]-1,
                              pctx->app_task_ID);
        // If the discovery was successful, proceed.
        if ( ret == SUCCESS )
          pctx->disc_state = ANCS_STORE_NS_DESCS_HANDLES;
        // If not, throw an error and invalidate the CCCD handle in the handle cache.
        else
        {
          LOG("ANCS_DISC_NS_DESCS FAILURE\n");
          pservie->chars_hdl[ANCS_NOTIF_SCR_HDL_START] = 0;
          pctx->disc_state = ANCS_DISC_FAILED;
          errorcode = 7;
        }
      }
    }
    break;

    // Store the retrieved Notification Source descriptors (namely, the CCCD).
    case ANCS_STORE_NS_DESCS_HANDLES:
    {
      LOG("Discovery Progress:\t7\n");
      LOG("Discovery State:\tStore the Notification Source's CCCD handle\n");

      // Wait for the discovery response.
      if (pMsg->method == ATT_FIND_INFO_RSP )
      {
        // Sanity check to validate that at least one descriptors pair was found,
        // and that the pair length is correct.
        if ( (pMsg->msg.findInfoRsp.numInfo > 0) &&
              (pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE) )
        {
          // This will keep track of the current pair being processed.
          uint8_t currentPair;

          // Iterate through the pair list.
          for(currentPair = 0; currentPair < pMsg->msg.findInfoRsp.numInfo; currentPair++)
          {
            // Check if the pair is a CCCD.
            uint16_t uuid = BUILD_UINT16(pMsg->msg.findInfoRsp.info.btPair[currentPair].uuid[0], pMsg->msg.findInfoRsp.info.btPair[currentPair].uuid[1]);
            if (uuid == GATT_CLIENT_CHAR_CFG_UUID)
            {
              // If so, store the handle in the handle cache, and proceed.
              pservie->chars_hdl[ANCS_NOTIF_SCR_HDL_CCCD] = pMsg->msg.findInfoRsp.info.btPair[currentPair].handle;
              pctx->disc_state = ANCS_DISC_DS_DESCS;
            }
          }
        }
      }
    }
    break;

    // Discover the Data Source's descriptors (namely, the CCCD) using the start
    // and end handle stored in the handle cache.
    case ANCS_DISC_DS_DESCS:
    {
      LOG("Discovery Progress:\t8\n");
      LOG("Discovery State:\tDiscover the Data Source's CCCD\n");

      // Wait until the Notification Source descriptors discovery has finished.
      if ( (pMsg->method == ATT_FIND_INFO_RSP) && (pMsg->hdr.status == bleProcedureComplete) )
      {

        // Discover ANCS Notification Source CCCD
        uint8_t discCheck = GATT_DiscAllCharDescs(pservie->conn_hdl,
                              pservie->chars_hdl[ANCS_DATA_SRC_HDL_START] + 1,
                              pservie->chars_hdl[ANCS_DATA_SRC_HDL_END],
                              pctx->app_task_ID);
        // If the discovery was successful, proceed.
        if (discCheck == SUCCESS )
          pctx->disc_state = ANCS_STORE_DS_DESCS_HANDLES;
        // If not, throw an error and invalidate the CCCD handle in the handle cache.
        else
        {
          LOG("ANCS_DISC_DS_DESCS FAILURE\n");
          pservie->chars_hdl[ANCS_DATA_SRC_HDL_CCCD] = 0;
          pctx->disc_state = ANCS_DISC_FAILED;
          errorcode = 8;
        }
      }
    }
    break;

    // Discover the Data Source's descriptors (namely, the CCCD) using the start
    // and end handle stored in the handle cache.
    case ANCS_STORE_DS_DESCS_HANDLES:
    {
      LOG("Discovery Progress:\t9\n");
      LOG("Discovery State:\tStore the Data Source's CCCD handle\n");

      // Wait for the discovery response.
      if (pMsg->method == ATT_FIND_INFO_RSP )
      {
        bool flg = FALSE;
        // Sanity check to validate that at least one descriptors pair was found,
        // and that the pair length is correct.
        if ( (pMsg->msg.findInfoRsp.numInfo > 0) && (pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE) )
        {
          // This will keep track of the current pair being processed.
          uint8_t currentPair;

          // Iterate through the pair list.
          for(currentPair = 0; currentPair < pMsg->msg.findInfoRsp.numInfo; currentPair++)
          {
            // Check if the pair is a CCCD.
            uint16_t uuid = BUILD_UINT16(pMsg->msg.findInfoRsp.info.btPair[currentPair].uuid[0], pMsg->msg.findInfoRsp.info.btPair[currentPair].uuid[1]);
            if (uuid == GATT_CLIENT_CHAR_CFG_UUID)
            {
              // If so, store the handle in the handle cache, and proceed to the subscription process.
              pservie->chars_hdl[ANCS_DATA_SRC_HDL_CCCD] = pMsg->msg.findInfoRsp.info.btPair[currentPair].handle;
              flg = TRUE;
            }
          }

          if(flg == FALSE){
            LOG("ANCS_STORE_DS_DESCS_HANDLES FAILURE\n");
            pservie->chars_hdl[ANCS_DATA_SRC_HDL_CCCD] = 0;
            pctx->disc_state = ANCS_DISC_FAILED;
            errorcode = 9;
          }
          else{
            pctx->disc_state = ANCS_ENABLE_NS_CCCD;
          }
        }
      }
    }
    break;
    case ANCS_ENABLE_NS_CCCD:
    {
      bStatus_t ret;
      if (pMsg->method == ATT_FIND_INFO_RSP && (pMsg->hdr.status == bleProcedureComplete)){
        ret = Ancs_CCCDConfig(pservie->chars_hdl[ANCS_NOTIF_SCR_HDL_CCCD], TRUE);
        if(ret != SUCCESS){
          LOG("ANCS_DATA_SRC_HDL_CCCD FAILURE\n");
          pservie->chars_hdl[ANCS_DATA_SRC_HDL_CCCD] = 0;
          pctx->disc_state = ANCS_DISC_FAILED;
          errorcode = 10;
          break;
        }
        pctx->disc_state = ANCS_ENABLE_DS_CCCD;
      }
    }

    case ANCS_ENABLE_DS_CCCD:
    {
      bStatus_t ret;
      if (pMsg->method == ATT_WRITE_RSP ){
        ret = Ancs_CCCDConfig(pservie->chars_hdl[ANCS_DATA_SRC_HDL_CCCD], TRUE);
        if(ret != SUCCESS){
          LOG("ANCS_DATA_SRC_HDL_CCCD FAILURE\n");
          pservie->chars_hdl[ANCS_DATA_SRC_HDL_CCCD] = 0;
          pctx->disc_state = ANCS_DISC_FAILED;
          errorcode = 10;
          break;
        }
        pctx->disc_state = ANCS_DISC_FINISH;
        pctx->app_state = ANCS_STATE_READY;
        //pctx->disc_state = ANCS_WAIT_CCCD_READY;
      }
    }
    break;
    case ANCS_WAIT_CCCD_READY:
    if (pMsg->method == ATT_WRITE_RSP ){
      pctx->disc_state = ANCS_DISC_FINISH;
      pctx->app_state = ANCS_STATE_READY;
      
      LOG("Discovery Progress:\t12\n");
      LOG("Discovery State:\tProcessing notification data\n");
      break;
    }
    default:
    {
      pctx->disc_state = ANCS_DISC_FAILED;
      errorcode = 11;
    }
    break;
  }
  if(errorcode != 0)
  {
    LOG("Discovery State:\tDiscovery Error: %d\n",errorcode);
  }
  return errorcode;
}

static uint8_t Ancs_CCCDConfig(uint16_t attrHdl, uint8_t isEnable)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  // Declare return variable status.
  uint8_t status;

  // Stores the GATT write request parameters.
  attWriteReq_t req;
  LOG("Ancs_CCCDConfig handle %d\n", attrHdl);



  // Else, prepare the request.
    // Set the data length to 2 ("01" = 2 bytes).
    req.len = 2;

    // If we are enabling notifications, set the write data to "01".
    if (isEnable == TRUE)
    {
      req.value[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
      req.value[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
    }

    // Else, disable notifications, thus set the write data to "00".
    else
    {
      req.value[0] = 0x00;
      req.value[1] = 0x00;
    }

    // Signature and command must be set to zero.
    req.sig = 0;
    req.cmd = 0;

    // Set the handle to the passed value (either the Notification Source's CCCD handle
    // or the Data Source's CCCD handle).
    req.handle = attrHdl;

    // Send write request. If it fails, free the memory allocated and
    // return a failure.
    status = GATT_WriteCharValue(pctx->ancs_service.conn_hdl, &req, pctx->app_task_ID);
    if ( status != SUCCESS){
      LOG("Ancs_CCCDConfig %d\n", status);
    }

  return status;
}

bStatus_t ble_ancs_start_descovery(uint16_t conn_handle)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  ancs_service_t* pservie = &(pctx->ancs_service);
  pservie->conn_hdl = conn_handle;
  pctx->disc_state = ANCS_DISC_SERVICE;
  pctx->app_state = ANCS_STATE_DISCOVERY;
  ble_disc_service(NULL);
  return SUCCESS;
}
static void ble_ancs_process_ds_notify(gattMsgEvent_t *pMsg)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  
  ancs_parse_get_attrs_response(pctx, (const uint8_t*)pMsg->msg.handleValueNoti.value, pMsg->msg.handleValueNoti.len);
  return;
}



void ble_ancs_process_ns_notify(gattMsgEvent_t *pMsg)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  ancs_evt_t evt;
  ancs_notify_evt_t notify_msg;
  uint8_t len = pMsg->msg.handleValueNoti.len;
  if (len != 8)
  {
    LOG("\n");
    LOG("Error evt len\n");
    return;
  }

  // Create pointer to GATT notification data.
  uint8_t *packetData = pMsg->msg.handleValueNoti.value;

  // Store the ANCS notification's eventID
  notify_msg.eventID = packetData[0];

  // Store the ANCS notification's eventFlag
  notify_msg.eventFlag = packetData[1];

  // Store the ANCS notification's categoryID
  notify_msg.categoryID = packetData[2];

  notify_msg.categoryCount = packetData[3];
  
  // Notification UID from packetData[4] to packetData[7]
  notify_msg.notifUID[0] = packetData[ANCS_NOTIF_UID_LENGTH];
  notify_msg.notifUID[1] = packetData[ANCS_NOTIF_UID_LENGTH+1];
  notify_msg.notifUID[2] = packetData[ANCS_NOTIF_UID_LENGTH+2];
  notify_msg.notifUID[3] = packetData[ANCS_NOTIF_UID_LENGTH+3];
  evt.msg = (void*)(&notify_msg);
  evt.len = sizeof(notify_msg);
  evt.type = BLE_ANCS_EVT_NOTIF;
  if(pctx->callback){
    pctx->callback(&evt);
  }

}



bStatus_t ble_ancs_get_notif_attrs(const uint8_t * pNotificationUID)
{
  return notif_attrs_get(&s_ancs_ctx, pNotificationUID);

}


bStatus_t ble_ancs_get_app_attrs(const uint8_t * p_app_id, uint8_t app_id_len)
{
  return app_attrs_get(&s_ancs_ctx, p_app_id, app_id_len);

}


bStatus_t ble_ancs_handle_gatt_event(gattMsgEvent_t* pMsg)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  ancs_service_t* pservie = &(pctx->ancs_service);
  if(pctx->app_state == ANCS_STATE_DISCOVERY)
  {
    ble_disc_service(pMsg);
  }
  else if (pMsg->method == ATT_HANDLE_VALUE_NOTI || pMsg->method == ATT_HANDLE_VALUE_IND)
  {
    // If we receive a GATT notification, we can assume it pertains to ANCS
    // because we only subscribe to notifications from the Notification Source
    // ancs Data Source.
    uint8_t notifHandle = pMsg->msg.handleValueNoti.handle;

    if ( notifHandle == pservie->chars_hdl[ANCS_NOTIF_SCR_HDL_START])
    {
      ble_ancs_process_ns_notify(pMsg);
    }
    else if ( notifHandle == pservie->chars_hdl[ANCS_DATA_SRC_HDL_START])
    {
      ble_ancs_process_ds_notify(pMsg);
    }
  }
  //If we have received a read or write response, assume that it is related to
  //CCCD configuration
  else if (pMsg->method == ATT_WRITE_RSP)
  {
  }
  // It's safe to free the incoming message
  return (TRUE);

}



bStatus_t ble_ancs_disconnect(void)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  uint8_t               app_task_ID = pctx->app_task_ID;
  ancs_evt_hdl_t        callback = pctx->callback;
  osal_memset(&s_ancs_ctx, 0, sizeof(s_ancs_ctx));
  pctx->app_task_ID = app_task_ID;
  pctx->callback = callback;
  return SUCCESS;
}

bStatus_t ble_ancs_init(ancs_evt_hdl_t evt_hdl, uint8_t task_ID)
{
  ancs_ctx_t* pctx = &s_ancs_ctx;
  osal_memset(&s_ancs_ctx, 0, sizeof(s_ancs_ctx));
  pctx->app_task_ID = task_ID;
  pctx->callback = evt_hdl;
  return SUCCESS;
}

